﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Design;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using OpenQuant.API;
using OpenQuant.API.Plugins;
using OpenQuant.Quik.Design;
using StockSharp.Algo;
using StockSharp.BusinessEntities;
using StockSharp.Logging;
using StockSharp.Quik;
using oqOrder = OpenQuant.API.Order;
using OrderStatus = OpenQuant.API.OrderStatus;
using ssOrder = StockSharp.BusinessEntities.Order;
using ssQuote = StockSharp.BusinessEntities.Quote;

namespace OpenQuant.Quik
{
    public class Provider: UserProvider
    {
        class SubscriptionInfo
        {
            /// <summary>
            /// Инструмент OpenQuant
            /// </summary>
            public Instrument Instrument { get; set; }
            /// <summary>
            /// Элементы информационной подписки
            /// </summary>
            public SubscriptionDataType Subscription { get; set; }
        }
        
        static readonly QuikTrader Trader = new QuikTrader();
        readonly LogManager _logManager = new LogManager();
        readonly string _settingsFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "OpenQuant.Quik.Settings.xml");

        readonly Dictionary<SecurityKey, SubscriptionInfo> _openQuantInstruments = new Dictionary<SecurityKey, SubscriptionInfo>(new SecurityKey());
        readonly Dictionary<SecurityKey, Security> _stockSharpInstruments = new Dictionary<SecurityKey, Security>(new SecurityKey());
        readonly Dictionary<SecurityKey, Quote> _lastQuotes = new Dictionary<SecurityKey, Quote>(new SecurityKey());
        readonly Dictionary<SecurityKey, double> _lastTrades = new Dictionary<SecurityKey, double>(new SecurityKey());
        readonly Dictionary<Instrument, Dictionary<decimal, ssQuote>> _lastBids = new Dictionary<Instrument, Dictionary<decimal, ssQuote>>();
        readonly Dictionary<Instrument, Dictionary<decimal, ssQuote>> _lastAsks = new Dictionary<Instrument, Dictionary<decimal, ssQuote>>();
        readonly object _instrumentsLocker = new object();

        readonly OrderHive _hive = new OrderHive();
        readonly object _hiveLocker = new object();
        
        public Provider()
        {
            id = 123;
            name = "Quik";
            description = "Провайдер для торгового терминала Quik";
            url = @"http://www.quik.ru/";
            
            //Базовые события
            Trader.Connected += () =>
            {
                Trader.StartExport();
                EmitConnected();
            };
            Trader.ConnectionError += (e => EmitError(e.Message));
            Trader.Disconnected += EmitDisconnected;
            Trader.ProcessDataError += (e => EmitError(e.Message));
            Trader.SecuritiesChanged += TraderSecuritiesChanged;
            Trader.NewTrades += TraderNewTrades;

            Trader.NewOrders += TraderNewOrders;
            Trader.OrdersChanged += TraderOrdersChanged;
            Trader.OrdersRegisterFailed += TraderOrdersRegisterFailed;
            Trader.OrdersCancelFailed += TraderOrdersCancelFailed;
            Trader.NewMyTrades += TraderNewMyTrades;
            Trader.NewStopOrders += TraderNewOrders;
            Trader.StopOrdersChanged += TraderOrdersChanged;

            //Загружаем настройки провайдера
            ProviderSettings = new Settings();
            try
            {
                ProviderSettings = Settings.Load(_settingsFile);
            }
            catch (Exception ex)
            {
                EmitError(string.Format("Невозможно загрузить настройки провайдера: {0}", ex.Message));
            }

            //Настраиваем объект Trader
            Trader.IsAsyncMode = true;
            Trader.TransactionIdGenerator = new IncrementTransactionIdGenerator { CurrentTransactionId = ProviderSettings.TransactionId - 1 };
            Trader.SecuritiesTable.Columns.Add(DdeSecurityColumns.ShortName);
            Trader.SecuritiesTable.Columns.Add(DdeSecurityColumns.BestBidPrice);
            Trader.SecuritiesTable.Columns.Add(DdeSecurityColumns.BestBidVolume);
            Trader.SecuritiesTable.Columns.Add(DdeSecurityColumns.BestAskPrice);
            Trader.SecuritiesTable.Columns.Add(DdeSecurityColumns.BestAskVolume);
            Trader.SecuritiesTable.Columns.Add(DdeSecurityColumns.LastTradeTime);
            Trader.SecuritiesTable.Columns.Add(DdeSecurityColumns.LastTradeValue);
            Trader.SecuritiesTable.Columns.Add(DdeSecurityColumns.LastTradeVolume);
            Trader.SecuritiesTable.Columns.Add(DdeSecurityColumns.Decimals);
            Trader.SecuritiesTable.Columns.Add(DdeSecurityColumns.ExpiryDate);
        }
        
        /// <summary>
        /// Настройки провайдера Quik
        /// </summary>
        [Category("Main"), DisplayName("Quik Settings"), Description("Настройки адаптера для Quik")]
        public Settings ProviderSettings { get; private set; }
        
        public static QuikTrader QuikTrader { get { return Trader; } }

        [Editor(typeof(InstrumentsListEditor), typeof(UITypeEditor))]
        [Category("Main"), DisplayName("Instruments import"), Description("Импорт инструментов из терминала Quik")]
        public object InstrumentsList
        {
            get { return null; }
        }

        #region Соединение, подписка на получение данных по инструментам

        protected override void Connect()
        {
            if (IsConnected) return;

            try
            {
                InitLog();
                ProviderSettings.TransactionId = Trader.TransactionIdGenerator.GetNextId();
                Settings.Save(ProviderSettings, _settingsFile);
                if (string.IsNullOrWhiteSpace(ProviderSettings.FullPath))
                {
                    EmitError("Путь к терминалу Quik не может быть пустым");
                    return;
                }
                Trader.Path = ProviderSettings.FullPath;
                Trader.NewMarketDepths += TraderMarketDepthsChanged;
                Trader.MarketDepthsChanged += TraderMarketDepthsChanged;
                Trader.Connect();
            }
            catch (Exception ex)
            {
                EmitError(ex.Message);
            }
        }

        protected override bool IsConnected
        {
            get { return (Trader != null) && Trader.IsConnected; }
        }

        protected override void Disconnect()
        {
            if (!IsConnected) return;

            try
            {
                ProviderSettings.TransactionId = Trader.TransactionIdGenerator.GetNextId();
                Settings.Save(ProviderSettings, _settingsFile);
                Trader.NewMarketDepths -= TraderMarketDepthsChanged;
                Trader.MarketDepthsChanged -= TraderMarketDepthsChanged;
                Trader.StopExport();
                Trader.Disconnect();
            }
            catch (Exception ex)
            {
                EmitError(ex.Message);
            }
        }

        protected override void Subscribe(Instrument instrument, SubscriptionDataType subscriptionDataType)
        {
            lock (_instrumentsLocker)
            {
                var key = new SecurityKey
                {
                    Board = instrument.AltIDGroups["Quik"].AltExchange,
                    Code = instrument.AltIDGroups["Quik"].AltSymbol
                };
                var security = Trader.Securities.FirstOrDefault(s => s.Class == key.Board && s.Code == key.Code);

                if (security == null) EmitError(string.Format("Инструмент {0} не найден в списке инструментов Quik", key));
                SubscriptionInfo info;
                if (_openQuantInstruments.TryGetValue(key, out info))
                    info.Subscription = info.Subscription | subscriptionDataType;
                else
                    _openQuantInstruments[key] = new SubscriptionInfo { Instrument = instrument, Subscription = subscriptionDataType };
                _stockSharpInstruments[key] = security;
                if (subscriptionDataType.HasFlag(SubscriptionDataType.OrderBook))
                    Trader.RegisterMarketDepth(security);
            }
        }

        protected override void Unsubscribe(Instrument instrument, SubscriptionDataType subscriptionDataType)
        {
            lock (_instrumentsLocker)
            {
                var key = new SecurityKey
                {
                    Board = instrument.AltIDGroups["Quik"].AltExchange,
                    Code = instrument.AltIDGroups["Quik"].AltSymbol
                };
                if (_openQuantInstruments.ContainsKey(key))
                {
                    if (subscriptionDataType.HasFlag(SubscriptionDataType.OrderBook))
                    {
                        Trader.UnRegisterMarketDepth(_stockSharpInstruments[key]);
                        EmitNewOrderBookUpdate(instrument, Clock.Now, BidAsk.Bid, OrderBookAction.Reset, 0, 0, 0);
                        EmitNewOrderBookUpdate(instrument, Clock.Now, BidAsk.Ask, OrderBookAction.Reset, 0, 0, 0);
                    }
                    var info = _openQuantInstruments[key];
                    info.Subscription = (SubscriptionDataType) (info.Subscription - subscriptionDataType);
                    if (!Enum.IsDefined(typeof(SubscriptionDataType), info.Subscription))
                    {
                        _openQuantInstruments.Remove(key);
                        _stockSharpInstruments.Remove(key);
                        _lastBids.Remove(instrument);
                        _lastAsks.Remove(instrument);
                    }
                }
            }
        }

        void TraderSecuritiesChanged(IEnumerable<Security> securities)
        {
            lock (_instrumentsLocker)
            {
                foreach (var security in securities)
                {
                    var key = new SecurityKey
                    {
                        Board = security.Class,
                        Code = security.Code
                    };

                    var exist = _openQuantInstruments.ContainsKey(key);
                    if (!exist) continue;
                    var info = _openQuantInstruments[key];
                    if (!info.Subscription.HasFlag(SubscriptionDataType.Quotes))
                        continue;

                    var newQuote = new Quote
                    {
                        AskPrice = (double)security.BestAsk.Price,
                        AskVolume = (int)security.BestAsk.Volume,
                        BidPrice = (double)security.BestBid.Price,
                        BidVolume = (int)security.BestBid.Volume
                    };
                    Quote oldQuote;
                    if (_lastQuotes.TryGetValue(key, out oldQuote))
                    {
                        if (!newQuote.Equals(oldQuote))
                        {
                            EmitNewQuote(info.Instrument, Clock.Now, id, newQuote.BidPrice, newQuote.BidVolume, newQuote.AskPrice, newQuote.AskVolume);
                            _lastQuotes[key] = newQuote;
                        }
                    }
                    else
                    {
                        EmitNewQuote(info.Instrument, Clock.Now, id, newQuote.BidPrice, newQuote.BidVolume, newQuote.AskPrice, newQuote.AskVolume);
                        _lastQuotes[key] = newQuote;
                    }
                }
            }
        }

        void TraderNewTrades(IEnumerable<StockSharp.BusinessEntities.Trade> trades)
        {
            lock (_instrumentsLocker)
            {
                foreach (var trade in trades)
                {
                    var key = new SecurityKey
                    {
                        Board = trade.Security.Class,
                        Code = trade.Security.Code
                    };
                    SubscriptionInfo info;
                    if (!_openQuantInstruments.TryGetValue(key, out info)) continue;
                    if (!info.Subscription.HasFlag(SubscriptionDataType.Trades)) continue;
                    EmitNewTrade(info.Instrument, Clock.Now, id, (double)trade.Price, (int)trade.Volume);
                    _lastTrades[key] = (double)trade.Price;
                }
            }
        }

        void TraderMarketDepthsChanged(IEnumerable<MarketDepth> marketDepths)
        {
            lock (_instrumentsLocker)
            {
                foreach (var marketDepth in marketDepths)
                {
                    var key = new SecurityKey
                    {
                        Board = marketDepth.Security.Class,
                        Code = marketDepth.Security.Code
                    };
                    
                    SubscriptionInfo info;
                    if (!_openQuantInstruments.TryGetValue(key, out info)) continue;
                    if (!info.Subscription.HasFlag(SubscriptionDataType.OrderBook)) continue;
                    var newBids = marketDepth.Bids.ToDictionary(d => d.Price);
                    var newAsks = marketDepth.Asks.ToDictionary(d => d.Price);
                    UpdateOrderBook(info.Instrument, BidAsk.Bid, newBids);
                    UpdateOrderBook(info.Instrument, BidAsk.Ask, newAsks);
                }
            }
        }

        void UpdateOrderBook(Instrument instrument, BidAsk bidAsk, Dictionary<decimal, ssQuote> newQuotes)
        {
            Dictionary<decimal, ssQuote> oldQuotes;
            var last = (bidAsk == BidAsk.Bid) ? _lastBids : _lastAsks;
            
            if (!last.TryGetValue(instrument, out oldQuotes))   //Обновляем стаканы первый раз
            {
                for (int i = 0; i < newQuotes.Count; i++)
                {
                    var element = newQuotes.ElementAt(i).Value;
                    EmitNewOrderBookUpdate(instrument, Clock.Now, bidAsk, OrderBookAction.Insert, (double)element.Price, (int)element.Volume, i);
                }
            }
            else
            {
                //Удаляем ненужные котировки (по цене)
                for (int i = 0; i < oldQuotes.Count; i++)
                {
                    var quote = oldQuotes.ElementAt(i);
                    if (!newQuotes.ContainsKey(quote.Value.Price))
                    {
                        EmitNewOrderBookUpdate(instrument, Clock.Now, bidAsk, OrderBookAction.Delete, 0, 0, i);
                        oldQuotes.Remove(quote.Key);
                        i--;
                    }
                }
                //Обновляем объемы у существующих записей (если необходимо)
                for (int i = 0; i < oldQuotes.Count; i++)
                {
                    var oldElement = oldQuotes.ElementAt(i);
                    var newElement = newQuotes[oldElement.Key];
                    if (oldElement.Value.Volume != newElement.Volume)
                        EmitNewOrderBookUpdate(instrument, Clock.Now, bidAsk, OrderBookAction.Update, (double)newElement.Price, (int)newElement.Volume, i);
                }
                //Добавляем новые записи
                for (int i = 0; i < newQuotes.Count; i++)
                {
                    var newElement = newQuotes.ElementAt(i);
                    if (!oldQuotes.ContainsKey(newElement.Key))
                        EmitNewOrderBookUpdate(instrument, Clock.Now, bidAsk, OrderBookAction.Insert, (double)newElement.Value.Price, (int)newElement.Value.Volume, i);
                }
            }
            if (newQuotes.Count > 0)
                last[instrument] = newQuotes;
        }

        #endregion

        #region Заявки и сделки

        protected override void Send(oqOrder order)
        {
            //Делаем по-умолчанию счет с адаптера
            if (string.IsNullOrWhiteSpace(order.Account))
                order.Account = ProviderSettings.Account;

            var quik = CreateOrder(order);
            try
            {
                Trader.RegisterOrder(quik);
                lock (_hiveLocker)
                    _hive.Set(order, quik);
            }
            catch (Exception ex)
            {
                EmitRejected(order, ex.Message);
            }
        }

        protected override void Cancel(oqOrder order)
        {
            lock (_hiveLocker)
            {
                OrderHive.OrderHiveType type;
                if (!_hive.TrySearch(order, out type)) return;

                var quik = _hive.GetQuik(order, type);
                if ((quik.IsMatched()) || (quik.IsCanceled())) return;
                EmitPendingCancel(order);
                try
                {
                    Trader.CancelOrder(quik);
                }
                catch (Exception ex)
                {
                    EmitCancelReject(order, order.Status, ex.Message);
                }
            }
        }

        protected override void Replace(oqOrder order, double newQty, double newPrice, double newStopPrice)
        {
            lock (_hiveLocker)
            {
                OrderHive.OrderHiveType type;
                if (!_hive.TrySearch(order, out type)) return;

                var quik = _hive.GetQuik(order, type);
                EmitPendingReplace(order);
                var newOrder = CreateOrder(order);
                newOrder.Volume = (int)newQty;
                switch (order.Type)
                {
                    case OrderType.Limit:
                        newOrder.Price = newOrder.Security.ShrinkPrice((decimal)newPrice);
                        break;
                    case OrderType.Stop:
                        ((QuikOrderCondition)newOrder.Condition).StopPrice = newOrder.Security.ShrinkPrice((decimal)newStopPrice);
                        break;
                    case OrderType.StopLimit:
                        newOrder.Price = newOrder.Security.ShrinkPrice((decimal)newPrice);
                        ((QuikOrderCondition)newOrder.Condition).StopPrice = newOrder.Security.ShrinkPrice((decimal)newStopPrice);
                        break;
                }

                var generator = (IncrementTransactionIdGenerator)Trader.TransactionIdGenerator;
                if ((quik.Security.ExchangeBoard == ExchangeBoard.Forts) && (quik.Condition == null))
                {
                    _hive.Set(generator.CurrentTransactionId + 1, order, quik);
                    quik = Trader.ReRegisterOrder(quik, newOrder.Price, newOrder.Volume);
                    _hive.Set(generator.CurrentTransactionId, order, quik);
                }
                else
                {
                    _hive.Set(generator.CurrentTransactionId + 2, order, newOrder);
                    Trader.ReRegisterOrder(quik, newOrder);
                }
            }
        }

        ssOrder CreateOrder(oqOrder order)
        {
            var rst = new ssOrder();
            lock (_instrumentsLocker)
            {
                var key = new SecurityKey
                {
                    Board = order.Instrument.AltIDGroups["Quik"].AltExchange,
                    Code = order.Instrument.AltIDGroups["Quik"].AltSymbol
                };

                Security instrument;
                if (_stockSharpInstruments.TryGetValue(key, out instrument))
                {
                    rst.Portfolio = Trader.Portfolios.FirstOrDefault(p => p.Name == order.Account);
                    rst.Security = instrument;
                    rst.Volume = (int)order.Qty;

                    switch (order.Side)
                    {
                        #region Покупка

                        case OrderSide.Buy:
                            rst.Direction = OrderDirections.Buy;
                            switch (order.Type)
                            {
                                case OrderType.Limit:
                                    rst.Type = OrderTypes.Limit;
                                    rst.Price = instrument.ShrinkPrice((decimal)order.Price);
                                    break;
                                case OrderType.Market:
                                    rst.Type = OrderTypes.Limit;
                                    rst.Price = (decimal)GetLastPrice(key) * (decimal)(1 + ProviderSettings.MarketOrderFactor);
                                    rst.Price = instrument.ShrinkPrice(rst.Price);
                                    break;
                                case OrderType.Stop:
                                    rst.Type = OrderTypes.Conditional;
                                    rst.Price = (decimal)order.StopPrice * (decimal)(1 + ProviderSettings.MarketOrderFactor);
                                    rst.Price = instrument.ShrinkPrice(rst.Price);
                                    var stop = new QuikOrderCondition
                                        {
                                            Type = QuikOrderConditionTypes.StopLimit,
                                            StopPrice = instrument.ShrinkPrice((decimal)order.StopPrice)
                                        };
                                    rst.Condition = stop;
                                    break;
                                case OrderType.StopLimit:
                                    rst.Type = OrderTypes.Conditional;
                                    rst.Price = instrument.ShrinkPrice((decimal)order.Price);
                                    var stoplimit = new QuikOrderCondition
                                    {
                                        Type = QuikOrderConditionTypes.StopLimit,
                                        StopPrice = instrument.ShrinkPrice((decimal)order.StopPrice)
                                    };
                                    rst.Condition = stoplimit;
                                    break;
                                default:
                                    EmitError("Данный тип заявки не поддерживается = " + order.Type);
                                    break;

                            }
                            break;

                        #endregion
                        #region Продажа

                        case OrderSide.Sell:
                            rst.Direction = OrderDirections.Sell;
                            switch (order.Type)
                            {
                                case OrderType.Limit:
                                    rst.Type = OrderTypes.Limit;
                                    rst.Price = instrument.ShrinkPrice((decimal)order.Price);
                                    break;
                                case OrderType.Market:
                                    rst.Type = OrderTypes.Limit;
                                    rst.Price = (decimal)GetLastPrice(key) * (decimal)(1 - ProviderSettings.MarketOrderFactor);
                                    rst.Price = instrument.ShrinkPrice(rst.Price);
                                    break;
                                case OrderType.Stop:
                                    rst.Type = OrderTypes.Conditional;
                                    rst.Price = (decimal)order.StopPrice * (decimal)(1 - ProviderSettings.MarketOrderFactor);
                                    rst.Price = instrument.ShrinkPrice(rst.Price);
                                    var stop = new QuikOrderCondition
                                    {
                                        Type = QuikOrderConditionTypes.StopLimit,
                                        StopPrice = instrument.ShrinkPrice((decimal)order.StopPrice)
                                    };
                                    rst.Condition = stop;
                                    break;
                                case OrderType.StopLimit:
                                    rst.Type = OrderTypes.Conditional;
                                    rst.Price = instrument.ShrinkPrice((decimal)order.Price);
                                    var stoplimit = new QuikOrderCondition
                                    {
                                        Type = QuikOrderConditionTypes.StopLimit,
                                        StopPrice = instrument.ShrinkPrice((decimal)order.StopPrice)
                                    };
                                    rst.Condition = stoplimit;
                                    break;
                                default:
                                    EmitError("Данный тип заявки не поддерживается = " + order.Type);
                                    break;
                            }
                            break;

                        #endregion
                    }
                }
                else
                    EmitError("Инструмент для создание приказа не найден");
            }
            return rst;
        }

        double GetLastPrice(SecurityKey key)
        {
            var rst = double.NaN;

            lock (_instrumentsLocker)
            {
                double tradePrice;
                if (_lastTrades.TryGetValue(key, out tradePrice))
                    rst = tradePrice;
                else
                {
                    Quote quote;
                    if (_lastQuotes.TryGetValue(key, out quote))
                        rst = (quote.BidPrice + quote.AskPrice) / 2;
                }
            }

            return rst;
        }

        void TraderNewOrders(IEnumerable<ssOrder> orders)
        {
            lock (_hiveLocker)
            {
                foreach (var quik in orders)
                {
                    OrderHive.OrderHiveType type;
                    if (!_hive.TrySearch(quik.TransactionId, out type)) continue;

                    var quant = _hive.GetQuant(quik.TransactionId, type);
                    switch (quant.Status)
                    {
                        case OrderStatus.PendingNew:
                            EmitAccepted(quant);
                            break;
                        case OrderStatus.PendingReplace:
                            EmitReplaced(quant);
                            break;
                    }
                }
            }
        }

        void TraderOrdersChanged(IEnumerable<ssOrder> orders)
        {
            lock (_hiveLocker)
            {
                foreach (var order in orders)
                {
                    OrderHive.OrderHiveType type;
                    if (!_hive.TrySearch(order.TransactionId, out type)) continue;

                    var quant = _hive.GetQuant(order.TransactionId, type);
                    switch (quant.Status)
                    {
                        case OrderStatus.PendingNew:
                            if (order.State == OrderStates.Active)
                                EmitAccepted(quant);
                            break;
                        case OrderStatus.PendingCancel:
                            if (order.IsCanceled())
                            {
                                EmitCancelled(quant);
                                _hive.Remove(quant, type);
                            }
                            break;
                    }
                    //Сработала стоп-заявка - переносим ее в группу лимитированных заявок
                    if ((order.DerivedOrder != null) && (type == OrderHive.OrderHiveType.Stop))
                    {
                        _hive.Remove(order.TransactionId, type);
                        _hive.Set(quant, order.DerivedOrder);
                    }
                }
            }
        }

        void TraderOrdersRegisterFailed(IEnumerable<OrderFail> orderFails)
        {
            lock (_hiveLocker)
            {
                foreach (var fails in orderFails)
                {
                    OrderHive.OrderHiveType type;
                    if (!_hive.TrySearch(fails.Order.TransactionId, out type)) continue;

                    var quant = _hive.GetQuant(fails.Order.TransactionId, type);
                    EmitRejected(quant, fails.Error.Message);
                    _hive.Remove(fails.Order.TransactionId, type);
                }
            }
        }

        void TraderOrdersCancelFailed(IEnumerable<OrderFail> orderFails)
        {
            lock (_hiveLocker)
            {
                foreach (var fails in orderFails)
                {
                    OrderHive.OrderHiveType type;
                    if (!_hive.TrySearch(fails.Order.TransactionId, out type)) continue;

                    var quant = _hive.GetQuant(fails.Order.TransactionId, type);
                    EmitCancelReject(quant, quant.Status, fails.Error.Message);
                    _hive.Remove(fails.Order.TransactionId, type);
                }
            }
        }

        void TraderNewMyTrades(IEnumerable<MyTrade> myTrades)
        {
            lock (_hiveLocker)
            {
                foreach (var myTrade in myTrades)
                {
                    OrderHive.OrderHiveType type;
                    if (!_hive.TrySearch(myTrade.Order.TransactionId, out type)) continue;

                    var quant = _hive.GetQuant(myTrade.Order.TransactionId, type);
                    EmitFilled(quant, (double)myTrade.Trade.Price, (int)myTrade.Trade.Volume);
                    if (quant.IsFilled)
                        _hive.Remove(quant, type);
                }
            }
        } 

        #endregion

        protected override void RequestHistoricalData(HistoricalDataRequest request)
        {
            EmitError("Quik не поддерживает получение исторических данных");
            EmitHistoricalDataCompleted(request);
        }

        protected override BrokerInfo GetBrokerInfo()
        {
            if (!IsConnected)
            {
                EmitError("Провайдер Quik не подключен");
                return new BrokerInfo();
            }
            
            var brockerInfo = new BrokerInfo();
            var accounts = Trader.Portfolios.OrderBy(p => p.Name);
            foreach (var account in accounts)
            {
                var a = brockerInfo.AddAccount(account.Name);
                a.AddField("ExchangeBoard", account.ExchangeBoard.ToString());
                a.AddField("Currency", account.Currency.ToString());
                a.AddField("Begin Value", account.BeginValue.ToString(CultureInfo.InvariantCulture));
                a.AddField("Blocked Value", account.BlockedValue.ToString(CultureInfo.InvariantCulture));
                a.AddField("Current Value", account.CurrentValue.ToString(CultureInfo.InvariantCulture));
                a.AddField("Leverage", account.Leverage.ToString(CultureInfo.InvariantCulture));
                foreach (var extension in account.ExtensionInfo)
                    a.AddField(extension.Key.ToString(), extension.Value.ToString());

                var account1 = account;
                var positions = Trader.Positions.Where(p => p.Portfolio == account1);
                foreach (var position in positions)
                {
                    var p = a.AddPosition();
                    p.Currency = account.Currency.ToString();
                    p.Exchange = position.Security.ExchangeBoard.ToString();
                    p.Qty = decimal.ToDouble(position.CurrentValue);
                    p.LongQty = (position.CurrentValue > 0) ? decimal.ToDouble(position.CurrentValue) : 0;
                    p.ShortQty = (position.CurrentValue < 0) ? decimal.ToDouble(Math.Abs(position.CurrentValue)) : 0;
                    p.Symbol = position.Security.Code;
                }

                var account2 = account;
                var orders = Trader.Orders.Where(d => d.Portfolio == account2);
                foreach (var order in orders)
                {
                    var d = a.AddOrder();
                    d.Currency = order.Portfolio.Currency.ToString();
                    d.Exchange = order.Security.ExchangeBoard.ToString();
                    if (order.Security.Class.Equals("SPBFUT"))
                        d.InstrumentType = InstrumentType.Futures;
                    else if (order.Security.Class.Equals("SPBOPT"))
                        d.InstrumentType = InstrumentType.Option;
                    else if (order.Security.Class.Equals("RTSISSUE"))
                        d.InstrumentType = InstrumentType.Stock;
                    else if (order.Security.Class.Equals("FUTARENA"))
                        d.InstrumentType = InstrumentType.Futures;
                    else if (order.Security.Class.Equals("FUTSPREAD"))
                        d.InstrumentType = InstrumentType.MultiLeg;
                    else if (order.Security.Class.Equals("RTSST"))
                        d.InstrumentType = InstrumentType.Stock;
                    else if (order.Security.Class.Equals("EQOB"))
                        d.InstrumentType = InstrumentType.Bond;
                    else if (order.Security.Class.Equals("EQOS"))
                        d.InstrumentType = InstrumentType.Bond;
                    else if (order.Security.Class.Equals("EQNL"))
                        d.InstrumentType = InstrumentType.Stock;
                    else if (order.Security.Class.Equals("EQNO"))
                        d.InstrumentType = InstrumentType.Bond;
                    else if (order.Security.Class.Equals("EQLV"))
                        d.InstrumentType = InstrumentType.Stock;
                    else if (order.Security.Class.Equals("EQOV"))
                        d.InstrumentType = InstrumentType.Bond;
                    else if (order.Security.Class.Equals("EQNE"))
                        d.InstrumentType = InstrumentType.Stock;
                    else if (order.Security.Class.Equals("EQNB"))
                        d.InstrumentType = InstrumentType.Bond;
                    else if (order.Security.Class.Equals("EQDE"))
                        d.InstrumentType = InstrumentType.Stock;
                    else if (order.Security.Class.Equals("EQDB"))
                        d.InstrumentType = InstrumentType.Bond;
                    else if (order.Security.Class.Equals("CETS"))
                        d.InstrumentType = InstrumentType.FX;
                    else if (order.Security.Class.Equals("INDX"))
                        d.InstrumentType = InstrumentType.Index;
                    else if (order.Security.Class.Equals("RTSIND"))
                        d.InstrumentType = InstrumentType.Index;
                    else if (order.Security.Class.Equals("RTSIDX"))
                        d.InstrumentType = InstrumentType.Index;
                    else if (order.Security.Class.Equals("EQDP"))
                        d.InstrumentType = InstrumentType.Stock;
                    else if (order.Security.Class.Equals("SMAL"))
                        d.InstrumentType = InstrumentType.Stock;
                    else if (order.Security.Class.Equals("GTSST"))
                        d.InstrumentType = InstrumentType.Stock;
                    else if (order.Security.Class.Equals("USDRUB"))
                        d.InstrumentType = InstrumentType.FX;
                    else
                        d.InstrumentType = InstrumentType.Stock;
                    d.OrderID = order.Id.ToString(CultureInfo.InvariantCulture);
                    d.Price = decimal.ToDouble(order.Price);
                    d.Qty = decimal.ToDouble(order.Volume);
                    d.Side = (order.Direction == OrderDirections.Buy) ? OrderSide.Buy : OrderSide.Sell;
                    
                    if ((order.State == OrderStates.Pending) && (order.IsMatchedEmpty()))
                        d.Status = OrderStatus.PendingNew;
                    else if ((order.State == OrderStates.Active) && (order.IsMatchedEmpty()))
                        d.Status = OrderStatus.New;
                    else if ((order.State == OrderStates.Active) && (order.IsMatchedPartially()))
                        d.Status = OrderStatus.PartiallyFilled;
                    else if (order.IsMatched())
                        d.Status = OrderStatus.Filled;
                    else if ((order.State == OrderStates.Done) && (order.IsCanceled()))
                        d.Status = OrderStatus.Cancelled;
                    else if (order.State == OrderStates.Failed)
                        d.Status = OrderStatus.Rejected;
                    
                    var condition = order.Condition as QuikOrderCondition;
                    d.StopPrice = (condition != null)
                                      ? (condition.StopPrice != null)
                                            ? decimal.ToDouble((decimal) condition.StopPrice)
                                            : 0.0
                                      : 0.0;
                    d.Symbol = string.Format("{0} [{1}]", order.Security.ShortName, order.Security.Code);
                    d.Type = (condition != null) ? OrderType.StopLimit : OrderType.Limit;
                }
            }

            return brockerInfo;
        }

        void InitLog()
        {
            foreach (var disposable in _logManager.Listeners.OfType<IDisposable>())
                disposable.Dispose();
            _logManager.Listeners.Clear();
            _logManager.Sources.Clear();
            
            if (!ProviderSettings.Log.Enabled) return;
            if (string.IsNullOrWhiteSpace(ProviderSettings.Log.Path))
            {
                EmitError("Не указан путь для лог-файла. Логирование отлключено");
                return;
            }
            _logManager.Listeners.Add(new FileLogListener(ProviderSettings.Log.Path));
            _logManager.Sources.Add(Trader);
        }
    }
}